/*
编写者：王出日
日期：2025，1，20
版本 0.4.2
描述：使用dio库实现的API调用类，包含了API请求、token管理、工具调用等功能

包含：
- BaseAPI: 基础API类，所有使用api访问大模型的类都继承自此类
- BaseAPIMultimodal: 多模态API类，继承自BaseAPI，增加了图片参数
*/

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';

/// API请求失败异常
class APIRequestFailed implements Exception {
  final String url;
  final int statusCode;
  final String errorDetails;

  APIRequestFailed({
    required this.url,
    required this.statusCode,
    required this.errorDetails,
  });

  @override
  String toString() {
    return 'APIRequestFailed: $errorDetails (Status: $statusCode, URL: $url)';
  }
}

/// 环境变量读取器
class EnvReader {
  final Map<String, String> _envVars = {};

  EnvReader({String? envFile}) {
    if (envFile != null) {
      _loadEnvFile(envFile);
    }
    // 也加载系统环境变量
    _envVars.addAll(Platform.environment);
  }

  void _loadEnvFile(String filePath) {
    try {
      final file = File(filePath);
      if (file.existsSync()) {
        final lines = file.readAsLinesSync();
        for (final line in lines) {
          if (line.trim().isNotEmpty && !line.startsWith('#')) {
            final parts = line.split('=');
            if (parts.length >= 2) {
              final key = parts[0].trim();
              final value = parts.sublist(1).join('=').trim();
              _envVars[key] = value;
            }
          }
        }
      }
    } catch (e) {
    }
  }

  String? getAPIKey() => _envVars['LLM_API_KEY'];
  String? getBaseUrl() => _envVars['BASE_URL'];
  String? getModel() => _envVars['MODEL_NAME'];
  int? getMaxInput() => int.tryParse(_envVars['MAX_INPUT'] ?? '');
  double? getTemperature() => double.tryParse(_envVars['TEMPERATURE'] ?? '');
}

/// Base API类，所有使用api访问大模型的类都继承自此类
/// 使用OpenAI格式的API请求，并提供token管理和工具调用功能
/// 优化了API调用方式，支持流式响应，并提供JSON格式模板
class BaseAPI {
  static const String apiEnvVarName = "LLM_API_KEY";
  static const String baseUrl = "";

  late final EnvReader envReader;
  late final String apiKey;
  late final String baseUrlEndpoint;
  late final String model;
  late final int maxInput;
  late final double temperature;
  late Dio dio;

  int token = 0;
  List<int> tokenList = [];
  String call = "API";

  BaseAPI({
    String? model,
    String? apiKey,
    String? baseUrl,
    String? envPath,
  }) {
    final envFile = envPath ?? '${Directory.current.path}/.env';
    envReader = EnvReader(envFile: envFile);

    try {
      this.apiKey = apiKey ?? envReader.getAPIKey() ?? '';
      baseUrlEndpoint = baseUrl ?? envReader.getBaseUrl() ?? '';
      this.model = model ?? envReader.getModel() ?? '';
      maxInput = envReader.getMaxInput() ?? 8000;
      temperature = envReader.getTemperature() ?? 1.0;
    } catch (e) {
      // 继续执行，但可能会在后续检查中失败
    }

    if (this.apiKey.isEmpty) {
      throw ArgumentError(
          "API key并没有在环境变量'$apiEnvVarName'和$envFile中找到，要么请你设置一下，要么输入apiKey参数");
    }
    if (baseUrlEndpoint.isEmpty) {
      throw ArgumentError(
          "Base_url并没有在环境变量'BASE_URL'和$envFile中找到，要么请你设置一下，要么输入baseUrl参数");
    }
    if (this.model.isEmpty) {
      throw ArgumentError(
          "模型名称并没有在环境变量'MODEL_NAME'和$envFile中找到，要么请你设置一下，要么输入model参数");
    }

    dio = Dio(BaseOptions(
      connectTimeout: Duration(seconds: 180),
      receiveTimeout: Duration(seconds: 180),
      headers: {"Content-Type": "application/json"},
    ));
  }

  /// 返回消耗的token数量
  int getTokens() => token;

  /// 调用大语言模型执行预测任务，支持单次对话和多轮对话模式
  Future<dynamic> predict({
    String? inputText,
    String sysPrompt = '你的工作非常的出色！',
    List<Map<String, dynamic>>? messages,
    double? temperature,
    double topP = 0.9,
    bool stream = false,
    String format = "text",
    String jsonFormat = '{}',
    List<Map<String, dynamic>>? tools,
    Map<String, dynamic>? kwargs,
  }) async {
    if (!stream) {
      // 处理非流式请求
      final temp = temperature ?? this.temperature;
      
      List<Map<String, dynamic>> messageList = messages ?? [];
      if (messages == null) {
        messageList = [
          {"role": "system", "content": sysPrompt}
        ];
        if (inputText != null && inputText.isNotEmpty) {
          messageList.add({"role": "user", "content": inputText});
        }
      }

      // 请求参数
      final formatDict = {
        'text': 'text',
        'json': 'json_object'
      };
      final requestFormat = formatDict[format] ?? 'text';

      final payload = <String, dynamic>{
        "model": model,
        "messages": messageList,
        "temperature": temp,
        "top_p": topP,
        "stream": stream,
      };

      if (tools != null && tools.isNotEmpty) {
        payload["tools"] = tools;
      }

      if (kwargs != null) {
        payload.addAll(kwargs);
      }

      try {
        Response response = await dio.post(
          baseUrlEndpoint,
          data: payload,
          options: Options(
            headers: {"Authorization": "Bearer $apiKey"},
          ),
        );

        Map<String, dynamic> responseData = response.data;
        token += (responseData["usage"]?["total_tokens"] ?? 0) as int;

        final result = <String, dynamic>{
          "role": "assistant",
          "content": responseData["choices"][0]["message"]["content"],
        };

        // 如果包含工具调用，添加 tool_calls
        final toolCalls = responseData["choices"][0]["message"]["tool_calls"];
        if (toolCalls != null && toolCalls.isNotEmpty) {
          final toolCall = toolCalls[0];
          result["tool_calls"] = {
            "id": toolCall["id"],
            "function": toolCall["function"],
          };
        }

        return result;
      } catch (e) {
        // 处理错误
        rethrow;
      }
    } else {
      // 处理流式请求 - 直接返回Stream
      return predictStream(
        inputText: inputText,
        sysPrompt: sysPrompt,
        messages: messages,
        temperature: temperature,
        topP: topP,
        stream: stream,
        format: format,
        jsonFormat: jsonFormat,
        tools: tools,
        kwargs: kwargs,
      );
    }
  }

  /// 流式响应解析器
  Stream<Map<String, dynamic>> predictStream({
    String? inputText,
    String sysPrompt = '你的工作非常的出色！',
    List<Map<String, dynamic>>? messages,
    double? temperature,
    double topP = 0.9,
    bool stream = true,
    String format = "text",
    String jsonFormat = '{}',
    List<Map<String, dynamic>>? tools,
    Map<String, dynamic>? kwargs,
  }) async* {
    final temp = temperature ?? this.temperature;
    
    List<Map<String, dynamic>> messageList = messages ?? [];
    if (messages == null) {
      messageList = [
        {"role": "system", "content": sysPrompt}
      ];
      if (inputText != null && inputText.isNotEmpty) {
        messageList.add({"role": "user", "content": inputText});
      }
    }

    // 请求参数
    final formatDict = {
      'text': 'text',
      'json': 'json_object'
    };
    final requestFormat = formatDict[format] ?? 'text';

    final payload = <String, dynamic>{
      "model": model,
      "messages": messageList,
      "temperature": temp,
      "top_p": topP,
      "stream": stream,
    };

    if (tools != null && tools.isNotEmpty) {
      payload["tools"] = tools;
    }

    if (kwargs != null) {
      payload.addAll(kwargs);
    }

    final Map<int, Map<String, dynamic>> toolCallsBuffer = {};
    List<Map<String, dynamic>>? finalToolCalls;
    final Map<int, String> receivedIds = {};
    final Set<int> toolNameSent = {};

    try {
      // 发起流式请求
      Response response = await dio.post(
        baseUrlEndpoint,
        data: payload,
        options: Options(
          headers: {"Authorization": "Bearer $apiKey"},
          responseType: ResponseType.stream,  // 设置为流式响应
        ),
      );
      
      // 处理流式响应
      Stream<String> lineStream = response.data.stream
          .cast<List<int>>()  // 关键修复：类型转换
          .transform(utf8.decoder)
          .transform(const LineSplitter());

      await for (String line in lineStream) {
        line = line.trim();
        if (line.isEmpty || !line.startsWith("data: ")) continue;  // 过滤控制帧
        
        String dataPart = line.substring(6);
        if (dataPart.isEmpty) continue;

        // 添加对[DONE]消息的处理
        if (dataPart == "[DONE]") {
          continue;
        }

        try {
          final data = json.decode(dataPart);
          final choices = data["choices"] as List?;
          if (choices == null) continue;

          for (final choice in choices) {
            final delta = choice["delta"] as Map<String, dynamic>?;
            if (delta == null) continue;

            // 处理普通内容
            final content = delta["content"] as String?;
            if (content != null && content.isNotEmpty) {
              yield {"role": "assistant", "content": content};
            }

            // 处理推理内容
            final reasoningContent = delta["reasoning_content"] as String?;
            if (reasoningContent != null && reasoningContent.isNotEmpty) {
              yield {
                "role": "assistant",
                "reasoning_content": reasoningContent,
                "content": ""
              };
            }

            // 处理工具调用
            final toolCalls = delta["tool_calls"] as List?;
            if (toolCalls != null) {
              for (final toolCall in toolCalls) {
                final index = toolCall["index"] as int;

                // 初始化缓冲区
                if (!toolCallsBuffer.containsKey(index)) {
                  toolCallsBuffer[index] = {
                    "index": index,
                    "function": {"arguments": ""},
                    "type": "",
                    "id": ""
                  };
                }

                // 保留首次收到的ID
                final id = toolCall["id"] as String?;
                if (id != null && !receivedIds.containsKey(index)) {
                  receivedIds[index] = id;
                }

                // 更新字段
                final current = toolCallsBuffer[index]!;
                current["id"] = receivedIds[index] ?? "";
                current["type"] = toolCall["type"] ?? current["type"];

                // 处理函数参数
                final function = toolCall["function"] as Map<String, dynamic>?;
                if (function != null) {
                  final currentFunction = current["function"] as Map<String, dynamic>;
                  currentFunction["name"] = function["name"] ?? currentFunction["name"];

                  // 如果这是第一次接收到工具名称且未发送过，发送工具名称
                  final toolName = currentFunction["name"] as String?;
                  if (toolName != null && toolName.isNotEmpty && !toolNameSent.contains(index)) {
                    toolNameSent.add(index);
                    yield {
                      "role": "assistant",
                      "content": "",
                      "tool_name": toolName
                    };
                  }

                  final arguments = function["arguments"] as String?;
                  if (arguments != null) {
                    currentFunction["arguments"] = (currentFunction["arguments"] as String) + arguments;
                  }
                }

                // 暂存当前状态
                final sortedEntries = toolCallsBuffer.entries.toList()
                  ..sort((a, b) => a.key.compareTo(b.key));
                finalToolCalls = sortedEntries.map((e) => e.value).toList();
              }
            }
          }
        } catch (e) {
          // JSON解析错误，跳过这一行
          continue;
        }
      }

      // 发送完整的工具调用
      if (finalToolCalls != null && finalToolCalls.isNotEmpty) {
        yield {
          "role": "assistant",
          "content": "",
          "tool_calls": finalToolCalls,
          "id": finalToolCalls.isNotEmpty ? finalToolCalls[0]["id"] ?? "" : ""
        };
      }
    } catch (e) {     
      rethrow;
    }
  }
}

/// 多模态API类，继承自BaseAPI，增加了图片参数
class BaseAPIMultimodal extends BaseAPI {
  BaseAPIMultimodal({
    required String model,
    String? apiKey,
    String? baseUrl,
    String? envPath,
  }) : super(
          model: model,
          apiKey: apiKey,
          baseUrl: baseUrl,
          envPath: envPath,
        );

  /// 将图片编码为base64格式
  String _encodeImage(String imagePath) {
    const allowedFormats = ['.png', '.jpg', '.jpeg', '.webp'];
    final lowerPath = imagePath.toLowerCase();
    
    if (!allowedFormats.any((ext) => lowerPath.endsWith(ext))) {
      throw ArgumentError('不支持的图片格式，仅支持${allowedFormats.join(', ')}');
    }

    try {
      final file = File(imagePath);
      final bytes = file.readAsBytesSync();
      return base64Encode(bytes);
    } catch (e) {
      throw Exception('图片读取失败: $e');
    }
  }

  /// 预测方法，支持图片输入
  @override
  Future<dynamic> predict({
    String? inputText,
    String? inputImage, // 多模态特有参数
    String sysPrompt = '你的工作非常出色！',
    List<Map<String, dynamic>>? messages,
    double? temperature,
    double topP = 0.9,
    bool stream = false,
    String format = "text",
    String jsonFormat = '{}',
    List<Map<String, dynamic>>? tools,
    Map<String, dynamic>? kwargs,
    int timeout = 60, // 多模态特有参数
  }) async {
    if (!stream) {
      // 处理非流式请求
      final temp = temperature ?? 0.3;
      List<Map<String, dynamic>> messageList = messages ?? [];
      
      if (messages == null) {
        messageList = [
          {"role": "system", "content": sysPrompt}
        ];

        final userContent = <Map<String, dynamic>>[];
        
        if (inputImage != null) {
          final extension = inputImage.split('.').last;
          userContent.add({
            "type": "image_url",
            "image_url": {
              "url": "data:image/$extension;base64,${_encodeImage(inputImage)}"
            }
          });
        }

        if (inputText != null && inputText.isNotEmpty) {
          userContent.add({"type": "text", "text": inputText});
        }

        if (userContent.isNotEmpty) {
          messageList.add({"role": "user", "content": userContent});
        }
      }

      final payload = <String, dynamic>{
        "model": model,
        "messages": messageList,
        "temperature": temp,
        "top_p": topP,
        "stream": stream,
      };

      if (tools != null && tools.isNotEmpty) {
        payload["tools"] = tools;
      }

      if (kwargs != null) {
        payload.addAll(kwargs);
      }

      final response = await dio.post(
        baseUrlEndpoint,
        data: payload,
        options: Options(
          headers: {"Authorization": "Bearer $apiKey"},
          receiveTimeout: Duration(seconds: timeout),
        ),
      );

      final responseData = response.data;
      token += (responseData["usage"]?["total_tokens"] ?? 0) as int;
      
      final result = <String, dynamic>{
        "role": "assistant",
        "content": responseData["choices"][0]["message"]["content"]
      };

      final toolCalls = responseData["choices"][0]["message"]["tool_calls"];
      if (toolCalls != null) {
        result["tool_calls"] = toolCalls;
      }

      return result;
    } else {
      // 处理流式请求
      return predictStream(
        inputText: inputText,
        sysPrompt: sysPrompt,
        messages: messages,
        temperature: temperature,
        topP: topP,
        stream: stream,
        format: format,
        jsonFormat: jsonFormat,
        tools: tools,
        kwargs: kwargs,
      ).first; // 返回第一个结果作为替代方案
    }
  }

  /// 多模态流式预测方法
  @override
  Stream<Map<String, dynamic>> predictStream({
    String? inputText,
    String? inputImage, // 多模态特有参数
    String sysPrompt = '你的工作非常出色！',
    List<Map<String, dynamic>>? messages,
    double? temperature,
    double topP = 0.9,
    bool stream = true,
    String format = "text",
    String jsonFormat = '{}',
    List<Map<String, dynamic>>? tools,
    Map<String, dynamic>? kwargs,
  }) async* {
    final temp = temperature ?? 0.3;
    List<Map<String, dynamic>> messageList = messages ?? [];
    
    if (messages == null) {
      messageList = [
        {"role": "system", "content": sysPrompt}
      ];

      final userContent = <Map<String, dynamic>>[];
      
      if (inputImage != null) {
        final extension = inputImage.split('.').last;
        userContent.add({
          "type": "image_url",
          "image_url": {
            "url": "data:image/$extension;base64,${_encodeImage(inputImage)}"
          }
        });
      }

      if (inputText != null && inputText.isNotEmpty) {
        userContent.add({"type": "text", "text": inputText});
      }

      if (userContent.isNotEmpty) {
        messageList.add({"role": "user", "content": userContent});
      }
    }

    final payload = <String, dynamic>{
      "model": model,
      "messages": messageList,
      "temperature": temp,
      "top_p": topP,
      "stream": stream,
    };

    if (tools != null && tools.isNotEmpty) {
      payload["tools"] = tools;
    }

    if (kwargs != null) {
      payload.addAll(kwargs);
    }

    // 调用父类的流式处理逻辑
    yield* super.predictStream(
      inputText: null, // 已经在messages中处理了
      sysPrompt: sysPrompt,
      messages: messageList,
      temperature: temperature,
      topP: topP,
      stream: stream,
      format: format,
      jsonFormat: jsonFormat,
      tools: tools,
      kwargs: kwargs,
    );
  }
}
